// 反射元数据
// Object.defineProperty  Object.setPrototypeof ... 修改了js执行时，执行过程
// Reflect.defineProperty Reflect.setPrototypeof   proxy/reflect

// 反射的概念就是可以修改程序执行的时候的行为

// 元数据： 描述数据的数据
// 修改执行代码的行为 对数据进行描述  Reflect.defineMetadata
//.......................................................................................
// import "reflect-metadata";

// class Eat {}
// 简化写法
// @Reflect.metadata("Class", "class matedata")
// class Animal {
// 简化写法
//   @Reflect.metadata("Class Property", "class property matedata")
//   static type = "animal";
// 简化写法
//   @Reflect.metadata("Proto method", "eat metadata")
// eat(val: Eat) {}
// eat(val: string):number {return 123}
// }
// 命令式代码， 声明式写法
// Reflect.defineMetadata("Class","Animal metadata1",Animal);
// Reflect.defineMetadata("Class", "Animal metadata2", Animal);
// Reflect.defineMetadata("class Property""type metadata", Animal, "type");
// Reflect.defineMetadata("Proto method", "eat metadata", Animal.prototype, "eat");
/*
// 上面代码其实,就是把上面的数据维护成一个weakmap
weakmap => {
    Animal:{
        undefined: {class:"class matedata"},
        type: {'Class Property':class property matedata}
    },
    Animal.prototype:{
        eat:{"Proto method":"eat metadata"}
    }
}
*/

// Reflect.defineMetadata("Proto method", "eat metadata", Animal.prototype, "eat");
// 这个是取映射关系的,key 映射实例 映射实例的key
// console.log(Reflect.getMetadata("Class", Animal));
// 这个是取映射关系的,key 映射实例 映射实例的key
// console.log(Reflect.getMetadata("Class Property", Animal, "type"));
// 也可以实例animal上拿到Animal.prototype的数据
// const animal = new Animal();
// 这个是取映射关系的,key 映射实例 映射实例的key
// console.log(Reflect.getMetadata("Proto method", animal, "eat"));

//..........................................................
//  ts生成额外的信息,需要tsconfig.json配置 "emitDecoratorMetadata": true,
// __metadata("design:type", Function),使用时,类型是什么,
// __metadata("design:paramtypes", []) 参数信息
// __metadata("design:returntype",void 0) 返回值

// metadata提取额外信息,类型拿到变量信息,进行校验
// console.log(Reflect.getMetadata("design:type", Animal.prototype, "eat"));
// console.log(Reflect.getMetadata("design:paramtypes", Animal.prototype, "eat"));
// console.log(Reflect.getMetadata("design:returntype", Animal.prototype, "eat"));
// ---------
import "reflect-metadata";

const REQUIRED_KEY = Symbol("REQUIRED_KEY");
const VALUEDATE_TYPE_KEY = Symbol();
function Required() {
  return function (target: any, key: string) {
    //先记录哪些属性是必填的，校验的时候来找这些属性是否有值
    // 先看原型上是否有值 target是原型
    const requireKeys = Reflect.getMetadata(REQUIRED_KEY, target) || [];
    // 在记录的时候不要给属性添加，如果后续校验没有这个属性,就找不到记录了
    // [...requireKeys, key],是因为Required会调用多次 ...requireKeys以前的  key新增的
    Reflect.defineMetadata(REQUIRED_KEY, [...requireKeys, key], target);
  };
}
enum Type {
  String = "string",
  Number = "number",
}
function ValueType(type: Type) {
  return (target: object, key: string) => {
    // 描述前属性的类型
    // target 表示原型
    Reflect.defineMetadata(VALUEDATE_TYPE_KEY, type, target, key);
  };
}

// 声明式
class Person {
  // Required表示必填
  @Required()
  name!: string; //  sequlize
  // Required表示必填
  @Required()
  age!: number;
}
// 生成实例
const person = new Person();
// 赋值属性
person.name = "abc";
person.age = 18;

function validate(intance: object) {
  // 获取实例对象上的属性  ['name', 'age']
  const existsProps = Reflect.ownKeys(person);
  // 获取必填属性 ['name', 'age']
  const requireKeys = Reflect.getMetadata(REQUIRED_KEY, intance);
  for (let key of requireKeys) {
    const validate_type = Reflect.getMetadata(VALUEDATE_TYPE_KEY, intance, key);

    if (validate_type) {
      if (typeof instance[key] !== validate_type) {
        throw new Error(key + " type 不正确");
      }
    }
    if (!existsProps.includes(key)) {
      throw new Error(key + " is required");
    }
  }
}
validate(person);
export {};
// js调用时,跳过监测的方法..................
// @ts-ignore 表示下一行是否正确,不管,不校验
// @ts-nocheck 放在文件最顶端,丧失对此文件的校验
// @ts-expect-error 表示知道下一行是错误的
vvv; // 不写@ts-expect-error,下一行会报错
// @ts-check jsdoc 来使用   放在文件最顶端,jsdoc 产生校验

// 装饰器加反射元数据可以做一些校验，收集代码逻辑后统一处理
